﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using Aurora.Domain;
using Aurora.Infrastructure;

namespace Aurora.DataAccess
{
    /// <summary>
    ///     Репозитарий, обеспечивающий слой логики для работы с картами пациентов.
    /// </summary>
    public class Repository : IRepository, IEquatable<object>, IDisposable
    {
        private readonly AuroraEntities _entities;
        private DomainObject _notExistingEntity;

        public Repository()
        {
            _notExistingEntity = new NotExistingEntity();
            _entities = new AuroraEntities();
        }

    #region Implementation of IRepository
        public DbContext GetContext()
        {
            return _entities;
        }

        public IList<T> GetObjects<T>() where T : DomainObject
        {
            _entities.Set<T>().Load();
            return _entities.Set<T>().Local.ToBindingList();
        }

        public IList GetObjects(Type domainType)
        {
            if (domainType.IsSubclassOf(domainType))
            {
                _entities.Set(domainType).Load();
                return _entities.Set(domainType).Local;
            }
            else
            {
                throw new TypedDataSetGeneratorException("Неподдерживаемый тип доменных объектов");
            }
        }

        public IList GetObjects(Func<DomainObject, DomainObject> query)
        {
            _entities.Set(typeof(DomainObject)).Load();
            return _entities.Set(typeof(DomainObject)) as IList;
        }

        public T GetObject<T>(long id) where T : DomainObject
        {
            try
            {
                _entities.Set<T>().Load();

                return _entities.Set<T>().Find(id);
            }
            catch (NullReferenceException)
            {
                Messenger.ShowErrorMessage("Ошибка при получении карты из БД",
                                           "Данная карта не существует или имеются проблемы с базой данных.");
                return _notExistingEntity as T;
            }
        }

        public bool Remove(DomainObject entity)
        {
            try
            {
                _entities.Set(entity.GetType()).Remove(entity);
            }
            catch (NullReferenceException)
            {
                Messenger.ShowErrorMessage("Ошибка при удалении карты из БД",
                                           "Данная карта не существует или имеются проблемы с базой данных.");
                return false;
            }

            return true;
        }

        public bool Update(DomainObject origEntity, DomainObject entity)
        {
            if (entity == _notExistingEntity) return false;

            try
            {
                _entities.Entry(origEntity).CurrentValues.SetValues(entity);
            }
            catch (NullReferenceException)
            {
                Messenger.ShowErrorMessage("Ошибка при обновление карты в БД",
                                           "Данная карта не существует или имеются проблемы с базой данных.");
                return false;
            }

            return true;
        }

        public bool Insert(DomainObject entity)
        {
            if (entity == _notExistingEntity) return false;

            try
            {
                _entities.Set(entity.GetType()).Add(entity);
            }
            catch (Exception exception)
            {
                Messenger.ShowErrorMessage("Ошибка при добавлении карты в БД",
                                           "Данная карта не существует или имеются проблемы с базой данных." + exception.Message);
                return false;
            }

            return true;
        }

        public bool SubmitChanges()
        {
            try
            {
                _entities.SaveChanges();
            }
            catch (OptimisticConcurrencyException)
            {
                _entities.SaveChanges();
            }
            return true;
        }

        public bool RejectChanges()
        {
            try
            {
                foreach (var dbEntityEntry in _entities.ChangeTracker.Entries())
                {
                    switch (dbEntityEntry.State)
                    {
                        case EntityState.Added:
                            {
                                _entities.Set(dbEntityEntry.Entity.GetType()).Local.Remove(dbEntityEntry);
                                break;
                            }
                        case EntityState.Deleted:
                            {
                                dbEntityEntry.State = EntityState.Unchanged;
                                break;
                            }
                        case EntityState.Modified:
                            {
                                dbEntityEntry.CurrentValues.SetValues(dbEntityEntry.OriginalValues);
                                break;
                            }
                    }
                }
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }

    #endregion

        #region Equals, GetHashCode and Dispose implementation
        public override bool Equals(object obj)
        {
            var other = obj as Repository;
            return other != null;
        }

        public override int GetHashCode()
        {
            const int hashCode = 0;
            return hashCode;
        }

        public static bool operator ==(Repository lhs, Repository rhs)
        {
            if (ReferenceEquals(lhs, rhs))
                return true;
            if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null))
                return false;
            return lhs.Equals(rhs);
        }

        public static bool operator !=(Repository lhs, Repository rhs)
        {
            return !(lhs == rhs);
        }

        public void Dispose()
        {
            lock (_entities)
            {
                _notExistingEntity = null;
                _entities.Dispose();
                GC.SuppressFinalize(this);
            }
        }

        void IDisposable.Dispose()
        {
            Dispose();		
        }
        #endregion
    }
}
